#!/usr/bin/env python
import optparse, sys, serial, time, re, string

#
# smsq - send and manage sms on a huawei 3g modem
# http://code.google.com/p/smsq/
#
# Pedro Cardoso (pcardoso@gmail.org)
# http://luminescente.com
# built on Mac OS X, should work on any other *nix
#
# got some help from
# - http://the.taoofmac.com/space/HOWTO/Setup%20GPRS/UMTS%20Access%20on%20a%20Mac
# - http://www.ibm.com/developerworks/aix/library/au-pythocli/
# - http://www.option.com/en/support/knowledge-base/article/how-to-send-an-sms-with-at-commands/
# - http://nunodantas.com/blog/
# - http://www.gsm-modem.de/gsm-modem-faq.html
#

DEBUG = 0
DELAY = 0.01
BAUDRATE = 115200

class Modem:
	"""Modem Class"""
	def __init__(self, com, debug = 1):
		self.DEBUG = debug
		try:
			# Initialize our com port:
			if self.DEBUG:
				print "Attempting to open your com port..."
			self.modem = serial.Serial(com, BAUDRATE)
			if self.DEBUG:
				print "Successfully opened the com port."
			# print self.modem         
			if (self.command("ATZ") != "OK"):
				raise
		except:
			if self.DEBUG:
				print "Whoops, failed to initialize your com port"
			raise
		return

	def command(self, cmd, wait = 1):
		"""Run command and return the response"""
		self.modem.write(cmd + "\r")
		if self.DEBUG:
			print "+++ " + cmd
		time.sleep(DELAY)
		response = ""
		if  not wait:
			return
		while self.modem.inWaiting():
			l = self.modem.readline().strip()
			if self.DEBUG:
				print "+++	   " + l
			if (l != cmd):
				response = response + l + "\n"
			time.sleep(DELAY)
		if self.DEBUG:
			print "--- " + response.strip() + " ---"
		return response.strip()

	def modem_exists(self):
		return self.modem_exists

	def set_textmode(self):
		if (self.command("AT+CMGF=1") != "OK"):
			raise
	
	def requested_pin(self):
		if re.search("\+CPIN\: SIM PIN", self.command("AT+CPIN?")):
			return True
		else:
			return False

	def set_pin(self, pin):
		ret = self.command("AT+CPIN=\"" + str(pin) + "\"")
		return True

	def get_network(self):
		if re.search("\+CREG\: 0,1", self.command("AT+CREG?")):
			ret = self.command("AT+COPS?")
			m = re.search('\+COPS\: .*?,.*?,"(.*)",.*?', ret)
			return m.group(1)
		return "NOT YET ATTACHED TO NETWORK"

	def get_signal_strength(self):
		"""docstring for get_signal_strength"""
		ret = self.command("AT+CSQ")
		m = re.search('\+CSQ\: (.*)', ret)
		return m.group(1)

	def set_smsc(self, smsc):
		if self.command("AT+CSCA=\"" + smsc + "\"") == "OK":
			return 1
		else:
			return 0

	def send_sms(self, to, message):
		self.command("AT+CMGS=\"" + to + "\"", False)
		self.command(message + chr(26))

	def sms_list(self):
		# could not figure out lookahead regex to do it all in one go. any help?
		self.command("")
		# time.sleep(2)
		ret = self.command("AT+CMGL=\"all\"")[:-3].split("+CMGL:")
		# ret = self.command("AT+CMGL=4")[:-3].split("+CMGL:")		
		p = re.compile('.*?([0-9]*?),"(.*?)","(.*?)",,"(.*?)"\n(.*)', re.DOTALL)
		m = []
		for s in ret:
			# skip empties
			if s.strip():
				matches = p.search(s)
				m.append(matches.groups())
		return m

	def sms_delete(self, id):
		# try to fetch it
		if re.search('\+CMGR\:', self.command("AT+CMGR=" + str(id) + "")):
			# delete
			self.command("AT+CMGD=" + str(id) + "")
			# try to fetch it again
			if not re.search('\+CMGR\:', self.command("AT+CMGR=" + str(id) + "")):
				return 1
		return 0

	
def main():
	p = optparse.OptionParser(description='Get and send SMSs from Huawei 3G Modem',
								   prog='smsq',
								   version='smsq 0.00000001',
								   usage='%prog')
	p.add_option('-p', '--pin', type='int', help='Specify PIN')
	p.add_option('-l', '--list', action='store_true', help='List messages')
	p.add_option('-d', '--delete', type='int', help='Delete SMS with given ID')
	p.add_option('-s', '--send', action='store_true', help='Send message (enclose in quotes!)')
	p.add_option('-c', '--smsc', type='string', help='Set SMS service center, with prefix (+351...)')	
	p.add_option('-t', '--to', type='string', help='Specify SMS recipient with prefix (+351...)')
	p.add_option('-m', '--message', type='string', help='Specify SMS text')
	p.add_option('-v', '--verbose', action ='store_true', help='Returns verbose output')
	p.add_option('-i', '--info', action='store_true', help='Show info')
	
	options, arguments = p.parse_args()
	
	global DEBUG
	if options.verbose:
		DEBUG = 1

	# print options
	# print arguments
                    
	if not (options.info or options.delete  != None or options.send or options.list):
		p.print_help()
		sys.exit()
				
	com_port = "/dev/cu.HUAWEIMobile-Pcui"
	try:
		modem = Modem(com_port, DEBUG)	
	except:
		p.error("No modem detected at specified port")		
		
	if modem.requested_pin():
		# print "PIN was requested"
		if options.pin:
			modem.set_pin(options.pin)
		else:
			p.error("PIN was requested but none given")

	modem.set_textmode()	
	
	# go through options
	if options.info:
		print "Network: " + modem.get_network()
		print "Signal strength: " + modem.get_signal_strength()
		print "SMS count: " + str(len(modem.sms_list()))		
	elif options.send:				
		if options.to and options.message and options.smsc:
			# print "setting SMSC to " + options.smsc
			if modem.set_smsc(options.smsc):
				if modem.send_sms(options.to, options.message):
					print "message sent"				
		else:
			p.error("incorrect number of arguments (-s needs -t and -m and -c)")
	elif options.delete != None:
		# print "delete message " + str(options.delete)
		if modem.sms_delete(options.delete):
			  print "SMS deleted"
		else:
			  print "SMS not deleted"		
	elif options.list:
		# print "list messages"
		sms = modem.sms_list()
		for m in sms:
			id, status, sender, date, text = m
			print "%s\t%s\t%s\t%s\t%s" % (id, status, sender, date, text.strip().replace('\n', '\\n'))
	else:
		p.print_help()

if __name__ == '__main__':
	main()